package babo.cd.physicalexperiment.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;

import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.UUID;

public class DeviceUuidUtils {
    protected static final String PREFS_FILE = "device_id.xml";
    protected static final String PREFS_DEVICE_ID = "device_id";

    protected static UUID uuid;

    public DeviceUuidUtils(Context context) {
        if (uuid == null) {
            final SharedPreferences prefs = context
                    .getSharedPreferences(PREFS_FILE, 0);
            final String id = prefs.getString(PREFS_DEVICE_ID, null);

            if (id != null) {
                // Use the ids previously computed and stored in the
                // prefs file
                uuid = UUID.fromString(id);

            } else {

                final String androidId = Settings.Secure
                        .getString(context.getContentResolver(),
                                Settings.Secure.ANDROID_ID);

                // Use the Android ID unless it's broken, in which case
                // fallback on deviceId,
                // unless it's not available, then fallback on a random
                // number which we store
                // to a prefs file
                try {
                    if (!"9774d56d682e549c".equals(androidId)) {
                        uuid = UUID.nameUUIDFromBytes(androidId
                                .getBytes("utf8"));
                    } else {
                        @SuppressLint("MissingPermission") final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
                        uuid = deviceId != null ? UUID
                                .nameUUIDFromBytes(deviceId
                                        .getBytes("utf8"))
                                : generateDeviceUuid(context);
                    }
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }

                // Write the value out to the prefs file
                prefs.edit()
                        .putString(PREFS_DEVICE_ID, uuid.toString())
                        .commit();

            }
        }
    }

    private UUID generateDeviceUuid(Context context) {

        // Get some of the hardware information
        String buildParams = Build.BOARD + Build.BRAND + Build.CPU_ABI
                + Build.DEVICE + Build.DISPLAY + Build.FINGERPRINT + Build.HOST
                + Build.ID + Build.MANUFACTURER + Build.MODEL + Build.PRODUCT
                + Build.TAGS + Build.TYPE + Build.USER;

        // Requires READ_PHONE_STATE
        TelephonyManager tm = (TelephonyManager) context
                .getSystemService(Context.TELEPHONY_SERVICE);

        // gets the imei (GSM) or MEID/ESN (CDMA)
        @SuppressLint("MissingPermission") String imei = tm.getDeviceId();

        // gets the android-assigned id
        String androidId = Settings.Secure.getString(context.getContentResolver(),
                Settings.Secure.ANDROID_ID);

        // requires ACCESS_WIFI_STATE
        WifiManager wm = (WifiManager) context
                .getSystemService(Context.WIFI_SERVICE);

        // gets the MAC address
        String mac = wm.getConnectionInfo().getMacAddress();

        // if we've got nothing, return a random UUID
        if (isEmpty(imei) && isEmpty(androidId) && isEmpty(mac)) {
            return UUID.randomUUID();
        }

        // concatenate the string
        String fullHash = buildParams + imei + androidId + mac;

        return UUID.nameUUIDFromBytes(fullHash.getBytes());
    }

    public UUID getDeviceUuid() {
        return uuid;
    }

    private static boolean isEmpty(Object s) {
        if (s == null) {
            return true;
        }
        if ((s instanceof String) && (((String) s).trim().length() == 0)) {
            return true;
        }
        if (s instanceof Map) {
            return ((Map<?, ?>) s).isEmpty();
        }
        return false;
    }
}
